#pragma once
#include "../Common/Common.h"

#define  DEFAULT_BUFFER_SIZE 4096
#define  MAX_STRING_SIZE 0xFFFF

struct ArrayBufferError
{

};

class ArrayBuffer
{
public:
	explicit ArrayBuffer(size_t s = DEFAULT_BUFFER_SIZE) : 
	m_writep(0), 
	m_readp(0),
	m_buffer_size(0)
	{
		if (s > 0)
		{
			m_buffer = (uint8*)malloc(s);
			ASSERT(m_buffer);
			if (NULL == m_buffer)
				throw std::bad_alloc();
		}

		m_buffer_size = s;
	}

	~ArrayBuffer()
	{
		free(m_buffer);
	}

	void write(uint8 v)
	{
		writeType(v);
	}

	void write(int8 v)
	{
		writeType(v);
	}

	void write(uint16 v)
	{
		writeType(v);
	}

	void write(int16 v)
	{
		writeType(v);
	}

	void write(uint32 v)
	{
		writeType(v);
	}

	void write(int32 v)
	{
		writeType(v);
	}

	void write(uint64 v)
	{
		writeType(v);
	}

	void write(int64 v)
	{
		writeType(v);
	}

	void write(float v)
	{
		writeType(v);
	}

	void write(double v)
	{
		writeType(v);
	}

	void write(const std::string& v)
	{
		ASSERT(v.size() <= MAX_STRING_SIZE);
		write(static_cast<uint16>(v.size()));
		if (!v.empty())
			writeBytes(v.data(),v.size());
	}

	void writeBytes(const void* bs, size_t s)
	{
		writeBytes((const uint8*)bs, s);
	}

	void writeBytes(const uint8* bs, size_t s)
	{
		ASSERT(s > 0);
		if (s == 0)
			return;

		if (m_buffer_size - m_writep < s)
		{
			grow(m_buffer_size+s);
		}

		ASSERT(m_buffer_size >= m_writep+s);

		uint8* b = m_buffer + m_writep;
		memcpy(b,bs,s);
		m_writep += s;
	}

	void read(uint8& v)
	{
		readType(v);
	}

	void read(int8& v)
	{
		readType(v);
	}

	void read(uint16& v)
	{
		readType(v);
	}

	void read(int16& v)
	{
		readType(v);
	}

	void read(uint32& v)
	{
		readType(v);
	}

	void read(int32& v)
	{
		readType(v);
	}

	void read(uint64& v)
	{
		readType(v);
	}

	void read(int64& v)
	{
		readType(v);
	}

	void read(float& v)
	{
		readType(v);
	}

	void read(double& v)
	{
		readType(v);
	}

	void read(std::string& v)
	{
		v.clear();

		uint16 s = 0;
		read(s);

		if (s <= 0)
			return;

		v.resize(s);
		int8* b = &*v.begin();
		readBytes(b,s);
	}

	void readBytes(void* bs, size_t s)
	{
		readBytes((uint8*)bs, s);
	}

	void readBytes(uint8* bs, size_t s)
	{
		ASSERT(s > 0);
		if (s == 0)
			return;

		if (m_writep - m_readp < s)
		{
			ASSERT(false);
			throw ArrayBufferError();
		}

		ASSERT(m_readp <= m_writep);
		ASSERT(m_readp <= m_buffer_size);

		const uint8* b = m_buffer + m_readp;
		memcpy(bs,b,s);
		m_readp += s;
	}

	size_t size() const
	{
		ASSERT(m_readp <= m_writep);
		return m_writep - m_readp;
	}

	void clear()
	{
		m_writep = 0;
		m_readp = 0;
	}

	bool empty() const
	{
		ASSERT(m_readp <= m_writep);
		return m_writep == m_readp;
	}

	const uint8* data() const
	{
		return m_buffer + m_readp;
	}

	uint8* gain(size_t s)
	{
		if (m_buffer_size - m_writep < s)
		{
			grow(m_buffer_size+s);
		}

		uint8* gbuf = m_buffer + m_writep;
		m_writep += s;

		return gbuf;
	}

 	typedef uint8* itreator;
 
 	itreator begin()
 	{
 		return m_buffer + m_readp;
 	}
 
 	itreator end()
 	{
 		return m_buffer + m_writep;
 	}

protected:
	template<typename T>
	void writeType(const T& v)
	{
		if (m_buffer_size - m_writep < sizeof(T))
		{
			grow(m_buffer_size+sizeof(T));
		}

		T* b = reinterpret_cast<T*>(m_buffer + m_writep);
		*b = v;

		m_writep += sizeof(T);
	}

	template<typename T>
	void readType(T& v)
	{
		if (m_buffer_size - m_readp < sizeof(T))
		{
			ASSERT(false);
			throw ArrayBufferError();
		}

		const T* b = reinterpret_cast<const T*>(&*begin());
		v = *b;
		m_readp += sizeof(T);
	}

	void grow(size_t s)
	{
		uint8* newbuffer = (uint8*)realloc(m_buffer,m_buffer_size + s);
		if (NULL == newbuffer)
			throw std::bad_alloc();

		m_buffer_size = m_buffer_size + s;
		m_buffer = newbuffer;
	}

private:
	size_t m_writep;
	size_t m_readp;

	uint8* m_buffer;
	size_t m_buffer_size;
};

template<typename T>
inline
ArrayBuffer& operator << (ArrayBuffer& ob, const T& value)
{
	ob.write(value);
	return ob;
}

template<typename T>
inline
ArrayBuffer& operator >> (ArrayBuffer& ob, T& value)
{
	ob.read(value);
	return ob;
}

// class IArrayBuffer
// {
// public:
// 	explicit IArrayBuffer(const uint8* buffer, size_t s, bool is_own=false) : 
// 		m_buffer(buffer),m_buffer_size(s), m_readp(0),m_is_own(is_own)
// 	{
// 	}
// 
// 	virtual ~IArrayBuffer()
// 	{
// 		if (m_is_own)
// 			delete []m_buffer;
// 	}
// 
// 
// 
// protected:
// 
// 
// private:
// 	size_t m_readp;
// 	const uint8* m_buffer;
// 	size_t m_buffer_size;
// 	bool m_is_own;
// 
// };

